package com.fourforfo.fourmall.product.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.fourforfo.common.constant.ProductConstant;
import com.fourforfo.common.to.SkuReductionTO;
import com.fourforfo.common.to.SkuStockTO;
import com.fourforfo.common.to.SpuBoundsTO;
import com.fourforfo.common.to.es.SkuEsModel;
import com.fourforfo.common.utils.R;
import com.fourforfo.fourmall.product.entity.*;
import com.fourforfo.fourmall.product.feign.SearchFeignService;
import com.fourforfo.fourmall.product.feign.SeckillFeignService;
import com.fourforfo.fourmall.product.feign.WareFeignService;
import com.fourforfo.fourmall.product.service.*;
import com.fourforfo.fourmall.product.feign.CouponFeignService;
import com.fourforfo.fourmall.product.vo.SpuSaveVO;
import com.fourforfo.fourmall.product.vo.spu.BaseAttrs;
import com.fourforfo.fourmall.product.vo.spu.Bounds;
import com.fourforfo.fourmall.product.vo.spu.Images;
import com.fourforfo.fourmall.product.vo.spu.Skus;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fourforfo.common.utils.PageUtils;
import com.fourforfo.common.utils.Query;

import com.fourforfo.fourmall.product.dao.SpuInfoDao;
import org.springframework.transaction.annotation.Transactional;


@Service("spuInfoService")
public class SpuInfoServiceImpl extends ServiceImpl<SpuInfoDao, SpuInfoEntity> implements SpuInfoService {

    @Autowired
    SpuInfoDescService spuInfoDescService;

    @Autowired
    SpuImagesService spuImagesService;

    @Autowired
    ProductAttrValueService productAttrValueService;

    @Autowired
    SkuInfoService skuInfoService;

    @Autowired
    SkuImagesService skuImagesService;

    @Autowired
    SkuSaleAttrValueService skuSaleAttrValueService;

    @Autowired
    CouponFeignService couponFeignService;

    @Autowired
    BrandService brandService;

    @Autowired
    CategoryService categoryService;

    @Autowired
    SpuInfoService spuInfoService;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    AttrService attrService;

    @Autowired
    SearchFeignService searchFeignService;

    @Autowired
    SeckillFeignService seckillFeignService;




    /**
     * 检索关键字：        key，        catelogId，brandId ,status
     * 对应数据库中column：id或spu_name catalog_id,brand_id,publish_status
     * catelogId是历史遗留原因，正确拼写应该是catalogId
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<SpuInfoEntity> queryWrapper = new QueryWrapper();
        String key = (String) params.get("key");
        String status = (String) params.get("status");
        String catelogId = (String) params.get("catelogId");
        String brandId = (String) params.get("brandId");
        if(!StringUtils.isEmpty(key)){
            queryWrapper.and(wrapper->{
                wrapper.eq("id",key).or().like("spu_name",key);
            });
        }
        if(!StringUtils.isEmpty(status)){
            queryWrapper.eq("publish_status",status);
        }
        if(!StringUtils.isEmpty(catelogId)&&!"0".equals(catelogId)){
            queryWrapper.eq("catalog_id",catelogId);
        }
        if(!StringUtils.isEmpty(brandId)&&!"0".equals(brandId)){
            queryWrapper.eq("brand_id",brandId);
        }

        IPage<SpuInfoEntity> page = this.page(
                new Query<SpuInfoEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    /**
     * TODO 高级部分再对请求超时等进行完善
     * 保存发布商品业务所提交的所有相关信息
     * @param spuSaveVO
     */
    @Transactional
    @Override
    public void saveSpuInfo(SpuSaveVO spuSaveVO) {
        //1. 保存spu基本信息 pms_spu_info
        SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
        BeanUtils.copyProperties(spuSaveVO,spuInfoEntity);
        spuInfoEntity.setCreateTime(new Date());
        spuInfoEntity.setUpdateTime(new Date());
        //1.1 调用保存方法
        this.saveBaseSpuInfo(spuInfoEntity);
        //1.2 判断是否保存成功，成功贼取出spuId用于后续数据库操作
        Long spuId = spuInfoEntity.getId();
        if(spuId==null){
            throw new NullPointerException("SpuInfoEntity保存失败,spu_id为null");
        }

        //2. 保存spu的描述图片的地址 pms_spu_info_desc
        //2.1 封装spu的描述图片的地址集合为一个String，默认使用符号 , 做分隔
        String decript = String.join(ProductConstant.Separator.ImagesUriSeparator.getSeparator(),spuSaveVO.getDecript());
        SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
        spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
        spuInfoDescEntity.setDecript(decript);
        spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);

        //3. 保存spu的图片集 pms_spu_images
        List<String> images = spuSaveVO.getImages();
        spuImagesService.saveImages(spuId,images);

        //4. 保存spu的规格参数 pms_product_attr_value
        List<BaseAttrs> baseAttrs = spuSaveVO.getBaseAttrs();
        productAttrValueService.saveBaseAttrs(spuId,baseAttrs);

        //5. 保存spu的积分信息 跨库：fourmall_sms-->sms_spu_bounds
        Bounds bounds = spuSaveVO.getBounds();
        SpuBoundsTO boundsTO = new SpuBoundsTO();
        BeanUtils.copyProperties(bounds,boundsTO);
        boundsTO.setSpuId(spuId);
        R r = couponFeignService.saveSpuBounds(boundsTO);
        if(r.getCode() != 0){
            log.error("远程保存spu积分信息失败");
        }

        //6. 保存当前spu对应的所有sku信息
        List<Skus> skusList = spuSaveVO.getSkus();
        if(skusList!=null&&skusList.size()>0){
            skusList.forEach((sku)->{
                //6.1 sku的基本信息 pms_sku_info
                SkuInfoEntity skuInfoEntity = new SkuInfoEntity();

                skuInfoEntity.setCatalogId(spuSaveVO.getCatalogId());
                skuInfoEntity.setBrandId(spuSaveVO.getBrandId());

                skuInfoEntity.setSpuId(spuId);
                skuInfoEntity.setSkuName(sku.getSkuName());
                skuInfoEntity.setSkuTitle(sku.getSkuTitle());
                skuInfoEntity.setSkuSubtitle(sku.getSkuSubtitle());
                skuInfoEntity.setPrice(sku.getPrice());
                skuInfoEntity.setSaleCount(0L);
                if(sku.getDescar()!=null&&sku.getDescar().size()>0){
                    skuInfoEntity.setSkuDesc(String.join(ProductConstant.Separator.DefaultSeparator.getSeparator(),sku.getDescar()));
                }
                for(Images img : sku.getImages()){
                    if(img.getDefaultImg() == 1) {
                        skuInfoEntity.setSkuDefaultImg(img.getImgUrl());
                    }
                }
                skuInfoService.saveSkusInfo(skuInfoEntity);


                //6.2 sku的图片信息 pms_sku_images
                Long skuId = skuInfoEntity.getSkuId();
                List<SkuImagesEntity> imagesEntities = sku.getImages().stream()
                        .filter(img->!StringUtils.isEmpty(img.getImgUrl()))
                        .map(img->{
                    SkuImagesEntity skuImagesEntity = new SkuImagesEntity();
                    skuImagesEntity.setSkuId(skuId);
                    skuImagesEntity.setImgUrl(img.getImgUrl());
                    skuImagesEntity.setDefaultImg(img.getDefaultImg());
                    return skuImagesEntity;
                }).collect(Collectors.toList());

                skuImagesService.saveBatch(imagesEntities);

                //6.3 sku的销售属性信息 pms_sku_sale_attr_value
                List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities = sku.getAttr().stream().map(attr->{
                    SkuSaleAttrValueEntity skuSaleAttrValueEntity = new SkuSaleAttrValueEntity();
                    BeanUtils.copyProperties(attr,skuSaleAttrValueEntity);
                    skuSaleAttrValueEntity.setSkuId(skuId);
                    return skuSaleAttrValueEntity;
                }).collect(Collectors.toList());
                skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntities);

                //6.4 sku的优惠、满减等信息 fourmall_sms-->sms_sku_ladder\sms_sku_full_reduction\sms_member_price
                SkuReductionTO skuReductionTO = new SkuReductionTO();
                BeanUtils.copyProperties(sku,skuReductionTO);
                skuReductionTO.setSkuId(skuId);
                if(skuReductionTO.getFullCount()>0 || new BigDecimal("0").compareTo(skuReductionTO.getFullPrice())==-1){
                    R r1 = couponFeignService.saveSkuReduction(skuReductionTO);
                    if(r1.getCode() != 0){
                        log.error("远程保存spu优惠信息失败");
                    }
                }

            });
        }

    }


    /**
     * 保存spu基本信息 pms_spu_info
     * @param spuInfoEntity
     */
    @Override
    public void saveBaseSpuInfo(SpuInfoEntity spuInfoEntity) {
        this.save(spuInfoEntity);
    }

    /**
     *  1. 商品上架即更新商品状态
     *  2. 将该商品信息加入到ElasticSearch
     * @param spuId pms_spu_info表的id
     */
    @Override
    public void upProduct(Long spuId) {
        if(spuId==null||spuId==0L){
            return ;
        }
        /**
         * 获取brandName，brandImg和categoryName
         */
        //获取spuInfo信息，查询该spuInfo所属的brand_id和catelog_id
        //注意：下面展示的三次查询可以简化成两次，查询品牌分类关系表可以获取冗余字段brandName和categoryName，再查询品牌表获取logo字段
        SpuInfoEntity spuInfoEntity = spuInfoService.getById(spuId);
        Long brandId = spuInfoEntity.getBrandId();
        Long catalogId = spuInfoEntity.getCatalogId();
        BrandEntity brandEntity = brandService.getById(brandId);
        CategoryEntity categoryEntity = categoryService.getById(catalogId);


        /**
         * 获取某个spu_id下能够被检索的所有attr
         * TODO 闲的时候思路好的时候可以来优化一下，限制：不适用联表查询
         * 注：使用联表查询可以一条sql解决，但是避免数据量很大时产生的笛卡尔积问题
         *     所以使用两次查询加过滤操作，可以根据业务数据量进行选择
         */
        //根据spu_id查询pms_product_attr_value
        List<ProductAttrValueEntity> productAttrValueEntities = productAttrValueService.list(
                new QueryWrapper<ProductAttrValueEntity>().eq("spu_id",spuId)
        );
        List<SkuEsModel.Attr> searchAttrs = new ArrayList<>();
        if(productAttrValueEntities!=null&&productAttrValueEntities.size()>0){
            //获取attrIds集合
            List<Long> attrIds = productAttrValueEntities.stream().map(
                    productAttrValueEntity -> productAttrValueEntity.getAttrId()
            ).collect(Collectors.toList());
            if(attrIds!=null&&attrIds.size()>0){
                //查询能够被检索的attrIds
                List<Long> searchAttrIds = attrService.listSearchAttrsByIds(attrIds);
                if(searchAttrIds!=null&&attrIds.size()>0){
                    Set<Long> searchAttrIdsSet = new HashSet<>(searchAttrIds);
                    //过滤出能够被检索的SkuEsModel.Attr
                    searchAttrs  = productAttrValueEntities.stream()
                            .filter(item -> searchAttrIdsSet.contains(item.getAttrId()))
                            .map(item->{
                                SkuEsModel.Attr attr = new SkuEsModel.Attr();
                                BeanUtils.copyProperties(item,attr);
                                return attr;
                            })
                            .collect(Collectors.toList());
                }

            }

        }



        //1. 查出当前的spuId对应的所有sku信息
        List<SkuInfoEntity> skuInfoEntities = skuInfoService.getSkusBySpuId(spuId);
        //收集skuIds发送批量查询请求,用于远程批量查询库存信息
        List<Long> skuIds = skuInfoEntities.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());

        /**
         * 需要给库存服务发送请求，查询是否有库存 wms_ware_sku，发送一个批量远程查询请求
         */
        //Map保存每个sku_id对应的库存状态
        HashMap<Long,Boolean> skuHasStockMap = null;//远程调用后map仍为null则说明远程调用失败
        try {
            R r = wareFeignService.listBySkus(skuIds);
            TypeReference<List<SkuStockTO>> typeReference = new TypeReference<List<SkuStockTO>>(){};//指定泛型类型
            //从R中取出从ware服务中返回的数据
            List<SkuStockTO> skuStockTOs = r.getData(typeReference);

            skuHasStockMap = new HashMap<>();
            for (SkuStockTO skuStockTO : skuStockTOs) {
                Boolean tempFlag = skuStockTO.getStock()!=null&&skuStockTO.getStock()>0?true:false;
                skuHasStockMap.put(skuStockTO.getSkuId(),tempFlag);
            }
        }catch (Exception e){
            log.error("库存服务查询异常:{}",e);
        }


        /**
         * 需要保存到ElasticSearch的商品属性集合
         */
        final List<SkuEsModel.Attr> attrs = searchAttrs;//声明一个final类型，不加final编译器会自己加
        HashMap<Long, Boolean> finalSkuHasStockMap = skuHasStockMap;//重新赋值一下，因为下面的lambda中不能使用变量
        List<SkuEsModel> skuEsModels = skuInfoEntities.stream().map(sku -> {
            //组装需要的数据
            SkuEsModel esModel = new SkuEsModel();
            BeanUtils.copyProperties(sku,esModel);
            //skuPrice,skuImg,hasStock,hotScore,brandName,brandImg,catelogName,attrs
            esModel.setSkuPrice(sku.getPrice());
            esModel.setSkuImg(sku.getSkuDefaultImg());
            //给skuEsModel设置库存状态
            if(finalSkuHasStockMap ==null){
                esModel.setHasStock(true);
            }else{
                Boolean status = finalSkuHasStockMap.get(sku.getSkuId());
                esModel.setHasStock(status==null?false:status);
            }


            //TODO hotScore,热度评分 设计业务通过点击量来更新热度评分,在此处默认设为0
            esModel.setHostScore(0L);

            //brandName,brandImg,catelogName
            if(!sku.getBrandId().equals(brandId)||!sku.getCatalogId().equals(catalogId)){
                log.error("pms_sku_info表中的brand_id和catelog_id必须与pms_spu_info中的保持一致,请排查出数据不一致的具体原因！");
                log.error("sku的brandId和catalogId,实际的brandId和catalogId"+" "+
                        sku.getBrandId()+" "+
                        sku.getCatalogId()+" "+
                        brandId+" "+
                        catalogId);
            }
            esModel.setBrandImg(brandEntity.getLogo());
            esModel.setBrandName(brandEntity.getName());
            esModel.setCatalogName(categoryEntity.getName());
            esModel.setCatalogId(catalogId);

            esModel.setAttrs(attrs);

            return esModel;
        }).collect(Collectors.toList());

        R r = searchFeignService.productStatusUp(skuEsModels);
        if(r.getCode() == 0){
            //远程调用成功
            //修改当前spuInfo的状态
            baseMapper.updateSpuStatus(spuId,ProductConstant.SpuStatusEnum.SPU_UP.getCode());
        }else{
            //远程调用失败
            //TODO 重复调用？接口幂等性;重试机制
        }


    }

    /**
     * 根据skuIds获取所有spuInfo信息，将spuInfo信息和对应的skuId保存到map中
     * @param skuIds
     * @return
     */
    @Override
    public Map<Long, SpuInfoEntity> listSpuInfoBySkuIds(List<Long> skuIds) {

        Map<Long,SpuInfoEntity> map = baseMapper.listSpuInfo(skuIds);

        return map;
    }

}